package cn.rgb.dao;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.LinkedList;
import java.util.Map;

public class ConnectionPool {

	private static final ThreadLocal<Connection> local = new ThreadLocal<Connection>();
	private LinkedList<Connection> connList = new LinkedList<Connection>();
	private String driver;
	private String url;
	private String user;
	private String password;
	private int initSize = 10;
	private boolean driverLoaded = false;
	
	public ConnectionPool(Map<String, String> dbConfig) {
		this.driver = dbConfig.get("JDBC.Driver");
		this.url = dbConfig.get("JDBC.ConnectionURL");
		this.user = dbConfig.get("JDBC.Username");
		this.password = dbConfig.get("JDBC.Password");
		String initSizeStr = dbConfig.get("");
		if(initSizeStr != null) {			
			this.initSize = Integer.parseInt(initSizeStr);
		}
		init(dbConfig);
	}
	
	public synchronized void init(Map<String, String> dbConfig) {
		try {
			if(!driverLoaded) {				
				Class.forName(driver);
			}
			driverLoaded = true;
			for(int iLoop = 0; iLoop < initSize; iLoop++) {				
				Connection conn = DriverManager.getConnection(url, user, password);
				Connection proxy = (Connection) Proxy.newProxyInstance(ConnectionPool.class.getClassLoader(), 
						conn.getClass().getInterfaces(), 
						new ConnectionInvocationHandler(conn));
				connList.add(proxy);
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
	
	public static void saveConnection(Connection conn) {
		local.set(conn);
	}
	
	public static Connection getCurrentConnection() {
		return local.get();
	}
	
	public synchronized Connection getConnection() {
		if(connList.isEmpty()) {
			try {
				this.wait(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		if(connList.isEmpty()) {
			throw new RuntimeException("There is no enough connection!");
		}
		return connList.removeFirst();
	}
	
	
	public synchronized void release() {
		for(Connection conn : connList) {
			if(conn != null)
				try {
					conn.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
		}
	}
	
	class ConnectionInvocationHandler implements InvocationHandler {

		private Connection target;
		
		public ConnectionInvocationHandler(Connection conn) {
			this.target = conn;
		}
		
		@Override
		public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
			if("close".equals(method.getName())) {
				local.set(null);
				connList.addLast((Connection) proxy);
				return null;
			}
			return method.invoke(target, args);
		}
		
	}
	
}
